/**
 * \file MemoryPool.cpp
 * \brief MemoryPool declaration class
 * \author Edouard ROGE
 * \date 06 august 2012
 */

#include <stdlib.h>
#include "Core/Log/LogManager.h"
#include <iostream>
#include "MemoryPool.h"

namespace Core
{
	namespace Memory
	{
		MemoryPool::MemoryPool()
			: m_pMemBlock(NULL), m_pAllocatedMemBlock(NULL), m_pFreeMemBlock(NULL), m_iUnitSize(0), m_iBlockSize(0)
		{
		}

		MemoryPool::MemoryPool(const MemoryPool & _other)
		{
			int32 nUnits = _other.m_iBlockSize / (_other.m_iUnitSize + sizeof(Unit));
			BuildPool(nUnits, _other.m_iUnitSize);
		}

		MemoryPool::~MemoryPool()
		{
			free(m_pMemBlock);
		}

		error MemoryPool::BuildPool(uint32 _iUnitNum, uint32 _iUnitSize)
		{
			LOG_INFO("New Memory Pool %d elements of size %d", _iUnitSize, _iUnitNum);
			m_iUnitSize = _iUnitSize;
			m_iBlockSize = _iUnitNum * (m_iUnitSize + sizeof(Unit));
			if (m_iBlockSize <= 0)
				return errInsufficientMemory;

			m_pMemBlock = malloc(m_iBlockSize);

			if (m_pMemBlock != NULL)
			{
				for (uint32 i = 0; i < _iUnitNum; ++i)
				{
					Unit * pCurrentUnit = (Unit*)((char*)m_pMemBlock + i*(m_iUnitSize + sizeof(Unit)));
					pCurrentUnit->prev = NULL;
					pCurrentUnit->next = m_pFreeMemBlock;

					if (m_pFreeMemBlock != NULL)
						m_pFreeMemBlock->prev = pCurrentUnit;

					m_pFreeMemBlock = pCurrentUnit;
				}
			}

			return errOK;
		}


		void * MemoryPool::Allocate(uint32 _iSize)
		{
			std::lock_guard<std::mutex> lock(m_mutex);
			if (_iSize > m_iUnitSize || NULL == m_pMemBlock || NULL == m_pFreeMemBlock)
			{
				return NULL;
			}


			Unit * pCurrentUnit = m_pFreeMemBlock;

			m_pFreeMemBlock = pCurrentUnit->next;

			if (NULL != m_pFreeMemBlock)
				m_pFreeMemBlock->prev = NULL;

			pCurrentUnit->next = m_pAllocatedMemBlock;

			if (NULL != m_pAllocatedMemBlock)
				m_pAllocatedMemBlock->prev = pCurrentUnit;

			m_pAllocatedMemBlock = pCurrentUnit;
			return (void *)((char *)pCurrentUnit + sizeof(Unit));
		}

		void MemoryPool::Free(void * _ptr)
		{
			std::lock_guard<std::mutex> lock(m_mutex);
			Unit *pCurrentUnit = (Unit *)((char *)_ptr - sizeof(Unit));

			m_pAllocatedMemBlock = pCurrentUnit->next;
			if (NULL != m_pAllocatedMemBlock)
				m_pAllocatedMemBlock->prev = NULL;

			pCurrentUnit->next = m_pFreeMemBlock;
			if (NULL != m_pFreeMemBlock)
				m_pFreeMemBlock->prev = pCurrentUnit;

			m_pFreeMemBlock = pCurrentUnit;
		}

		uint32 MemoryPool::GetPoolSize() const
		{
			return m_iBlockSize;
		}

		void MemoryPool::GetMemoryLeaks(std::vector<void*> & _res) const
		{
			for (void* i = m_pMemBlock; i < (void*)((char*)m_pMemBlock + m_iBlockSize); i = (void*)((char*)i + m_iUnitSize + sizeof(Unit)))
			{
				void * address = (void*)((char*)i + sizeof(Unit));
				if (!IsAddressFree(address))
					_res.push_back(address);
			}
		}

		void MemoryPool::Flush()
		{

		}

		bool MemoryPool::IsAddressBelongingTo(void * _ptr) const
		{
			return (m_pMemBlock < _ptr && _ptr < (void *)((char *)m_pMemBlock + m_iBlockSize));
		}

		bool MemoryPool::IsAddressFree(void * _ptr) const
		{
			Unit * pCurrentUnit = m_pFreeMemBlock;
			while (pCurrentUnit != NULL && _ptr != (void*)((char*)pCurrentUnit + sizeof(Unit)))
				pCurrentUnit = pCurrentUnit->next;

			return pCurrentUnit != NULL;
		}
	}
}